﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Hn.Command;
using Hn.Database.DAL;
using Hn.Database.Enum;
using Hn.Tool;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MSocketCore.Hub;
using MSocketCore.Model;
using SuperSocket;
using SuperSocket.Command;

namespace MSocketCore.MSocket
{
    [MyCmdFilter]
    [Command(Key = "10")]
    public class AFN10 : IAsyncCommand<MySession,MyPackage>
    {
        private readonly MessageClient _client;
        private readonly IHostEnvironment _env;
        private readonly ILogger<AFN10> _logger;
        private readonly IOptions<MConfig> _config;

        private List<byte> UpgradeFile { get; set; }
        private int Len { get; } = 200;
        private int Count { get; set; }
        private int _interval = 1000;


        public AFN10(MessageClient client, IHostEnvironment env ,ILogger<AFN10> logger,IOptions<MConfig> config)
        {
            _client = client;
            _env = env;
            _logger = logger;
            _config = config;
        }


        public ValueTask ExecuteAsync(MySession session, MyPackage info)
        {
            _logger.LogInformation($"App Recevice:{info.Sn}[Transmit]\n{info.Message}");

            var dtu = DtuDAL.GetBySn(info.Sn);
            var msg = "";
            var name = UserDAL.GetUserIdBySn(info.Sn);
            switch (info._Fn)
            {
                case 10:
                    var content = info.Content[0];
                    var ok = content >> 6;
                    if (ok == 1)
                    {
                        //var path = _env.ContentRootPath + "/file/upgrade.bin";
                        var path = _config.Value.BinPath;
                        _client.SendInfo(name, path);
                        LoadFile(path);
                        _client.SendInfo(name, UpgradeFile.Count.ToString());

                        var cmdlist = SendStep2(dtu.Sn);
                        _client.SendInfo(name, "Pakage Complete");

                        var count = cmdlist.Count;

                        for (int i = 0; i < count; i++)
                        {
                            msg = dtu.Sn + "Upgrade:Send" + (i + 1) + "/" + count + "";
                            LogHelper.Info(msg);
                            _client.SendInfo(name, msg);

                            session.SendAsync(cmdlist[i].ToArray());
                            Thread.Sleep(_interval);

                        }
                        var confirm = SendStep3(dtu.Sn);
                        session.SendAsync(confirm);

                        msg = dtu.Sn + "Upgrade:Confirm";
                        LogHelper.Info(msg);
                        _client.SendInfo(name, msg);
                    }

                    break;

                case 12:
                    var status = info.Content[0] >> 6;
                    if (status == 0x01)
                    {
                        var packet = new MyRequestInfo
                        {
                            Sn = dtu.Sn,
                            Afn = (byte)AFN.Tranfer,
                            Fn = CmdHelper.GetFn(13)
                        };
                        session.SendAsync(packet.Packet());

                        msg = dtu.Sn + "Upgrade:Complete";
                        LogHelper.Info(msg);
                        _client.SendInfo(name, msg);
                    }
                    else
                    {
                        var path = _env.ContentRootPath + "/upload/upgrade.bin";
                        LoadFile(path);
                        Count = (int)Math.Ceiling((decimal)UpgradeFile.Count / Len);

                        var packets = GetLostPacket(info.Content.Skip(1).ToArray());
                        //过滤多余的包
                        packets = packets.Where(p => p < Count).ToList();

                        for (int i = 0; i < packets.Count; i++)
                        {
                            var lostpacket = new MyRequestInfo
                            {
                                Afn = (byte)AFN.Tranfer,
                                Fn = CmdHelper.GetFn(11),
                                PSeq = (byte)(i + 1),
                                Sn = dtu.Sn
                            };

                            var list = new List<byte>();
                            list.AddRange(BitConverter.GetBytes(packets[i] + 1).Take(2));
                            var arr = UpgradeFile.Skip(packets[i] * Len).Take(Len).ToArray();
                            list.AddRange(BitConverter.GetBytes(arr.Length).Take(1));
                            list.AddRange(arr);
                            list.AddRange(DataPlus(arr));
                            lostpacket.Content = list.ToArray();
                            session.SendAsync(lostpacket.Packet());
                            Thread.Sleep(_interval);

                            msg = dtu.Sn + "Upgrade:Add " + (i + 1) + "/" + packets[i] + "/" + packets.Count + "";
                            LogHelper.Info(msg);
                            _client.SendInfo(name, msg);
                        }

                        var confirm = SendStep3(dtu.Sn);

                        session.SendAsync(confirm);
                        msg = dtu.Sn + "Upgrade:Confirm";
                        LogHelper.Info(msg);
                        _client.SendInfo(name, msg);
                    }

                    break;
            }

            return new ValueTask();
        }

        private List<int> GetLostPacket(byte[] buff)
        {
            var list = new List<int>();
            for (var i = 0; i < buff.Length; i++)
                for (var j = 0; j < 8; j++)
                {
                    var f = (buff[i] >> j) & 0x1;
                    if (f == 0x01) list.Add(i * 8 + j);
                }

            return list;
        }

        public void LoadFile(string path)
        {
            using (var fs = File.Open(path, FileMode.Open))
            {
                var buff = new byte[fs.Length];
                fs.Read(buff, 0, buff.Length);
                UpgradeFile = buff.ToList();
            }
        }

        public List<List<byte>> SendStep2(string sn)
        {
            Count = (int)Math.Ceiling((decimal)UpgradeFile.Count / Len);

            var datalist = new List<List<byte>>();
            for (var i = 0; i < Count; i++)
            {
                var packet = new MyRequestInfo();
                packet.Afn = (byte)AFN.Tranfer;
                packet.Pn = new byte[] { 0x00, 0x00 };
                packet.Fn = CmdHelper.GetFn(11);
                packet.Sn = sn;
                packet.PSeq = (byte)(i + 1);
                var list = new List<byte>();

                list.AddRange(BitConverter.GetBytes(i + 1).Take(2));
                var arr = UpgradeFile.Skip(i * Len).Take(Len).ToArray();
                list.AddRange(BitConverter.GetBytes(arr.Length).Take(1));

                list.AddRange(arr);
                list.AddRange(CmdHelper.GetCRCMuilt(arr).Take(2));

                packet.Content = list.ToArray();

                datalist.Add(packet.Packet().ToList());
            }

            return datalist;
        }

        public byte[] SendStep3(string sn)
        {
            var packet = new MyRequestInfo();
            packet.Afn = (byte)AFN.Tranfer;
            packet.Pn = new byte[] { 0x00, 0x00 };
            packet.Fn = CmdHelper.GetFn(12);
            packet.Sn = sn;
            var list = new List<byte>();

            list.AddRange(GetBytes2(UpgradeFile.Count));
            list.AddRange(GetBytes(Count));
            list.AddRange(DataPlus(UpgradeFile.ToArray()));
            packet.Content = list.ToArray();
            return packet.Packet();;
        }

        private byte[] GetBytes(int i)
        {
            var b = BitConverter.GetBytes(i);
            return new[]
            {
                b[0], b[1]
            };
        }

        private byte[] GetBytes2(int i)
        {
            var b = BitConverter.GetBytes(i);
            return new[]
            {
                b[0], b[1], b[2]
            };
        }

        private byte[] DataPlus(byte[] buff)
        {
            var value = 0;
            for (var i = 0; i < buff.Length; i++) value += buff[i];

            return GetBytes(value);
        }
    }
}
